package dongchel.vrp.localsearch;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import dongchel.vrp.entity.City;
import dongchel.vrp.entity.GlobalInformation;
import dongchel.vrp.entity.Route;
import dongchel.vrp.entity.Solution;
import dongchel.vrp.utils.CollectionUtils;

public class LocalSearch {
	/**
	 * define several static method as local search operation
	 */
	public static void twoExchange(Route route, int i, int j){
		Collections.reverse(route.getCityList().subList(i+1, j));
		route.refreshGeneral();
	}
	
	public static void randTwoExchange(Solution solution){
		Random rand = new Random();
		int routeIndex = (int) Math.floor(Math.random()*(solution.getRouteList().size()));
		Route route = solution.getRouteList().get(routeIndex);
		int iIndex = rand.nextInt(route.getCityList().size()-2)+1;
		int jIndex = rand.nextInt(route.getCityList().size()-iIndex-1)+iIndex;
		if(iIndex >= jIndex){
			randTwoExchange(solution);
		}else{
			twoExchange(route,iIndex,jIndex);
		}
	}
	
	public static void orExchange(Route fromRoute, int fromIndex, int slotSize, Route toRoute, int toIndex){
		/*if(fromRoute.getId() == toRoute.getId()){
			for(int i=0;i<slotSize;i++){
				Collections.swap(fromRoute.getCityList(), i+fromIndex, i+toIndex);
			}
			toRoute.getCityList().get(toIndex);
		}else{*/
			List<City> subCityList = copyEmptyList(fromRoute.getCityList().subList(fromIndex, fromIndex+slotSize));
				
			fromRoute.getCityList().removeAll(subCityList);
			/*for(int i=fromIndex;i<fromIndex+slotSize;i++){
				fromRoute.getCityList().remove(i);
			}*/
			fromRoute.refreshGeneral();
			toRoute.getCityList().addAll(toIndex,subCityList);
			toRoute.refreshGeneral();
		//}		
	}
	
	public static void randOrExchange(Solution solution){
		Random rand = new Random();
		int slotSize = rand.nextInt(3)+1;
			
		int fromRouteIndex = rand.nextInt(solution.getRouteList().size());
		Route fromRoute = solution.getRouteList().get(fromRouteIndex);
		
		if(fromRoute.getCityList().size()-3<slotSize){
			slotSize = fromRoute.getCityList().size()-3;
		}
		
		
		
		int fromIndex = rand.nextInt(fromRoute.getCityList().size()-2-slotSize)+1;
		
		int toRouteIndex = rand.nextInt(solution.getRouteList().size());
		Route toRoute = solution.getRouteList().get(toRouteIndex);
		
		int toIndex = 0;
		if(toRouteIndex == fromRouteIndex){
			toIndex = rand.nextInt(fromRoute.getCityList().size()-2-slotSize)+1;
		}else{
			toIndex = rand.nextInt(toRoute.getCityList().size()-2)+1;	
		}
		
		orExchange(fromRoute, fromIndex, slotSize, toRoute, toIndex);

		/*if(fromRouteIndex == toRouteIndex && fromIndex == toIndex){
			randOrExchange(solution);
		}else{
			orExchange(fromRoute, fromIndex, slotSize, toRoute, toIndex);
		}*/
	}
	
	public static void relocation(Route route, int i, int j){
		City cityI = route.getCityList().get(i);
		route.getCityList().remove(i);
		if(i<j){
			route.getCityList().add(j,cityI);
		}else{
			route.getCityList().add(j+1,cityI);
		}
		route.refreshGeneral();	
	}
	
	public static void randRelocation(Solution solution){
		Random rand = new Random();
		int routeIndex = (int) Math.floor(Math.random()*(solution.getRouteList().size()));
		Route route = solution.getRouteList().get(routeIndex);
		int iIndex = rand.nextInt(route.getCityList().size()-2)+1;
		int jIndex = rand.nextInt(route.getCityList().size()-2)+1;
		if(iIndex == jIndex){
			randRelocation(solution);
		}else{
			relocation(route,iIndex,jIndex);
		}
	}
	
	public static void exchange(Route route, int i, int j){
		Collections.swap(route.getCityList(), i, j);
		route.refreshGeneral();
	}
	
	public static void randExchange(Solution solution){
		Random rand = new Random();
		int routeIndex = (int) Math.floor(Math.random()*(solution.getRouteList().size()));
		Route route = solution.getRouteList().get(routeIndex);
		int iIndex = rand.nextInt(route.getCityList().size()-2)+1;
		int jIndex = rand.nextInt(route.getCityList().size()-2)+1;
		if(iIndex == jIndex){
			randExchange(solution);
		}else{
			exchange(route,iIndex,jIndex);
		}
	}
	
	public static void crossover(Route fromRoute, int fromIndex, Route toRoute, int toIndex){
		List<City> subCityList1 = copyEmptyList(fromRoute.getCityList().subList(fromIndex+1, fromRoute.getCityList().size()));
		
		List<City> subCityList2 = copyEmptyList(toRoute.getCityList().subList(toIndex+1, toRoute.getCityList().size()));

		fromRoute.getCityList().removeAll(subCityList1);

		toRoute.getCityList().removeAll(subCityList2);

		
		City firstCity1 = GlobalInformation.cityList.get(0).clone();
		firstCity1.setArrivalTime(0);
		fromRoute.getCityList().add(0,firstCity1);
		fromRoute.getCityList().addAll(subCityList2);
		
		City firstCity2 = GlobalInformation.cityList.get(0).clone();
		firstCity2.setArrivalTime(0);
		toRoute.getCityList().add(0,firstCity2);
		toRoute.getCityList().addAll(subCityList1);
		
		fromRoute.refreshGeneral();
		toRoute.refreshGeneral();
	}
	
	public static void randCrossover(Solution solution){
		Random rand = new Random();
		int fromRouteIndex = rand.nextInt(solution.getRouteList().size());	
		int toRouteIndex = rand.nextInt(solution.getRouteList().size());
		
		if(fromRouteIndex == toRouteIndex){
			randCrossover(solution);
		}else{
			Route fromRoute = solution.getRouteList().get(fromRouteIndex);
			int fromIndex = rand.nextInt(fromRoute.getCityList().size()-2)+1;
			
			Route toRoute = solution.getRouteList().get(toRouteIndex);
			int toIndex = rand.nextInt(toRoute.getCityList().size()-2)+1;
			crossover(fromRoute, fromIndex, toRoute, toIndex);
		}
	}
	
	public static List copyEmptyList(List<City> originalList){
		List<City> emptyList = new ArrayList<City>();
		for(int i=0;i<originalList.size();i++){
			City city = originalList.get(i);
			emptyList.add(city.clone());
		}
		return emptyList;
	}
	
}
